//#requires jDev

var $ajax = jDev.module('jDev.ajax', {

  _requests: new Array(),
  _transport: null,
  _callback: null,
  _states: ['Uninitialized','Loading','Loaded','Interactive','Complete'],

  '@init': function() {
    var transport = null;

    try {
      transport = new XMLHttpRequest();
    } catch (e) {
      try {
        transport = new ActiveXObject("Msxml2.XMLHTTP");
      } catch (e) {
        try {
          transport = new ActiveXObject("Microsoft.XMLHTTP");
        } catch (e) { }
      }
    }
    $ajax._transport = transport;
  },
  
  getXHRObject: function() {
    var xhrObj = false;
    try {
      xhrObj = new XMLHttpRequest();
    }
    catch(e){
      var progid = ['MSXML2.XMLHTTP.5.0', 'MSXML2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP'];
      for ( var i=0; i < progid.length; ++i ) {
        try {
          xhrObj = new ActiveXObject(progid[i]);
        }
        catch(e) {
          continue;
        }
        break;
      }
    }
    finally {
      return xhrObj;
    }
  },
  
  
  /**
   * Realiza uma requisição ajax.
   * 
   * @name request
   * @param {Object}   options               Opções da requisição.  
   * @param {String}   options.url           URL a ser requisitada.
   * @param {String}   options.type          Tipo de requisição ("get" ou "post"). O padrão é "get".
   * @param {Boolean}  options.async         Indica se a requisição é assíncrona. O padrão é "true".   
   * @param {String}   options.dataType      Dado requisitado ("text", "html", "xml" ou "json"). O padrão é "text".
   * @param {String}   options.contentType   ContentType a ser usado. O padrão é "application/x-www-form-urlencoded".  
   * @param {Function} options.onLoading     Função a ser executada antes da requisição ser enviada.
   * @param {Function} options.onLoaded      Função a ser executada logo que a requisição for enviada.
   * @param {Function} options.onInteractive Função a ser executada durante o recebimento da requisição.
   * @param {Function} options.onComplete    Função a ser executada ao completar a requisição.
   * @param {Function} options.onUpdate      Função a ser executada após completar a requisição.
   * @param {Function} options.onSuccess     Função a ser executada ao completar a requisição com sucesso.
   * @param {Function} options.onError       Função a ser executada ao completar a requisição com erro.
   */      
  request: function(options) {
    var o = options || {};

    // Configura as opções que não foram definidas para o seu valor padrão
    o.type = o.type && o.type.toLowerCase() || 'get';
    o.async = o.async || true;
    o.dataType = o.dataType || 'text'; 
    o.contentType = o.contentType || 'application/x-www-form-urlencoded';

    $ajax._requests.push(o);

    var s = $ajax._getState();
    if (s == 'Uninitialized' || s == 'Complete') 
      $ajax._sendRequest();
  },
  
  serialize: function(data) {
    var r = [''], rl = 0;
    if (data) {
      if (typeof data == 'string')  r[rl++] = data
        
      else if (data.innerHTML && data.elements) {
        for (var i=0,el,l=(el=data.elements).length; i < l; i++)
          if (el[i].name) {
            r[rl++] = encodeURIComponent(el[i].name); 
            r[rl++] = '=';
            r[rl++] = encodeURIComponent(el[i].value);
            r[rl++] = '&';
          }
            
      } else 
        for(param in data) {
          r[rl++] = encodeURIComponent(param); 
          r[rl++] = '=';
          r[rl++] = encodeURIComponent(data[param]);
          r[rl++] = '&';
        }
    }
    return r.join('').replace(/&$/, "");
  },

  _sendRequest: function() {
    var t = $ajax._transport, r = $ajax._requests.shift(), data;

    // Abre o objeto XMLHttpRequest
    t.open(r.type, r.url, r.async);

    //setRequestHeaders();

    // Registra o objeto para que o servidor saiba que é uma requisição AJAX
    t.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

    // Caso tenha sido informado algum dado
    if (data = $ajax.serialize(r.data))
      t.setRequestHeader('Content-Type', r.contentType);

    /** @ignore */
    // Tratamento de evento de mudança de estado
    t.onreadystatechange = function(){ 
        $ajax._onStateChange(r); 
      }; 

    // Envia a requisição
    t.send(data);
  },

  /**
   * Função de tratamento da mudança de estado da requisição ajax.
   */     
  _onStateChange: function(options) {
    var fn, o = options, t = $ajax._transport;
    var state = $ajax._getState(t); 

    if (fn = o['on' + state]) fn($ajax._getResponse(o), o);

    if (state == 'Complete') {
      var success = t.status == 200, response = $ajax._getResponse(o);

      if (fn = o['onUpdate'])
        fn(response, o);

      if (fn = o['on' + (success ? 'Success' : 'Failure')])
        fn(response, o);

      t.onreadystatechange = jDev.emptyFn;

      if ($ajax._requests.length > 0) 
        setTimeout($ajax._sendRequest, 10);
    }
  },

  /**
   * Retorna a resposta de acordo com o tipo de dado requisitado.
   */  
  _getResponse: function(options) {
    var t = $ajax._transport, type = options.dataType;

    if      (t.status != 200) return t.statusText
    else if (type == 'text')  return t.responseText
    else if (type == 'html')  return t.responseText
    else if (type == 'xml')   return t.responseXML
    else if (type == 'json')  return eval('(' + t.responseText + ')');
  },

  /**
   * Retorna o atual estado da requisição ajax.
   */     
  _getState: function() {
    return $ajax._states[$ajax._transport.readyState];
  }
  
});